/* Declarations for init operations.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_INITOP__
#define __KP_INITOP__   1

#include "defs.hpp"

KP_DECLS_BEGIN

struct interpreter;
struct init_op;

struct init_op_list
{
  dlist ops;
  const char *errmsg;

  init_op_list ()
    {
      this->ops.init_head ();
      this->errmsg = nullptr;
    }

  inline void add (init_op *op);

  static init_op_list& global_ops ();
  bool call (interpreter *interp);
};

struct init_op
{
  enum
    {
      st_init,
      st_wip,
      st_done
    };

  enum
    {
      result_ok,
      result_circular,
      result_failed
    };

  int state;
  int (*cb) (interpreter *);
  const char *name;
  dlist link;

  template <class ...Args>
  static int call_deps (interpreter *interp, Args... args)
    {
      init_op *deps[] = { args... };
      int ret = result_ok;

      for (auto p : deps)
        if ((ret = p->call (interp)) != result_ok)
          break;

      return (ret);
    }

  init_op (int (*cb) (interpreter *), const char *nm) :
      state (st_init), cb (cb), name (nm)
    {
      init_op_list::global_ops().add (this);
    }

  static int fail (const char *msg)
    {
      init_op_list::global_ops().errmsg = msg;
      return (init_op::result_failed);
    }

  int call (interpreter *interp)
    {
      if (this->state == st_wip)
        return (result_circular);
      else if (this->state == st_done)
        return (result_ok);

      this->state = st_wip;
      int ret = this->cb (interp);
      if (ret == result_ok)
        this->state = st_done;

      return (ret);
    }
};

void init_op_list::add (init_op *op)
{
  this->ops.add (&op->link);
}

// Initialize the full khipu API.
KP_EXPORT bool khipu_init ();

KP_DECLS_END

#endif
